<?php

class NoSuchObjectException extends Exception {}

class Model
{
	protected $columns=array();
	protected $data=array();
	protected $table_name="";
	
	public function __construct($id=0)
	{
		$this->columns=database()->describe($this->table_name);
		$this->set_defaults();
		
		$this->id=$id;

		if ($this->id != 0)
		{
			$this->load();
		}
	}
	
	public function set_values($array=NULL)
	{
		if ($array === NULL) $array = $_POST;
		foreach($array as $key=>$value)
		{
			$this->$key = $this->undbify($key, $value);
		}	
	}
	
	public function is_new()
	{
		return $this->id == 0;
	}
	
	public function __set($name, $value)
	{
		$this->data[$name] = $value;
	}
	
	public function __get($name)
	{
		if (array_key_exists($name, $this->data))
		{
			return $this->data[$name];
		}
	}
	
	public function save()
	{
		$db = database();
		$query="";
		if ($this->is_new())
		{
			$query="INSERT INTO $this->table_name (" . $this->list_columns() . ") VALUES (" . $this->list_values() . ")";
			$db->execute($query);
			$this->id = $db->last_inserted_id();
		}
		else
		{
			$querybits = array();
			$query="UPDATE $this->table_name SET ";
			foreach($this->columns as $column_name=>$column)
			{
				$value=$this->dbify($column_name);
				$querybits[] = "$column_name = $value";
			}
			$query.= implode($querybits, ', ');
			$query.= " WHERE id = $this->id";
			$db->execute($query);
		}
	}
	
	public function delete()
	{
		if (!$this->is_new())
		{
			database()->execute("DELETE FROM $this->table_name WHERE id=$this->id");
		}
	}
		
	private function load()
	{
		$db=database();
		
		if ($result=($db->get_row("SELECT * FROM $this->table_name WHERE id = " . $db->sanitize("$this->id"))))
		{
			foreach($result as $column=>$value)
			{
				$this->data[$column] = $this->undbify($column, $value);
			}
		}
		else
		{
			throw new NoSuchObjectException("No " . get_class($this) . " with id=$this->id found.");
		}
	}
	
	private function set_defaults()
	{
		foreach ($this->columns as $column)
		{
			$this->set_default($column['Field']);
		}
	}
	
	private function list_columns()
	{
		$list=array();
		foreach($this->columns as $column)
		{
			$fieldname=$column['Field'];
			if ($fieldname !== 'id')
			{
				$list[] = $fieldname;
			}
		}
		return implode($list, ',');
	}
	
	private function list_values()
	{
		$list=array();
		foreach($this->columns as $column)
		{
			$fieldname=$column['Field'];
			if ($fieldname !== 'id')
			{
				$list[] = $this->dbify($fieldname);
			}
		}
		return implode($list, ',');
	}
	
	private function set_default($column_name)
	{
		$column=$this->columns[$column_name];
		$default=NULL;
		$type_ar=explode('(', $column['Type']);
		$type=strtolower($type_ar[0]);
		switch($type)
		{
			//default for numeric types is 0;
			case 'bit':
			case 'int':
			case 'smallint':
			case 'tinyint':
			case 'mediumint':
			case 'bigint':
			case 'real':
			case 'float':
			case 'double':
			case 'decimal':
			case 'numeric':
				$default=0;
			break;
			
			// default for string types is '';
			case 'char':
			case 'varchar':
			case 'text':
				$default='';
			break;
			
			// everyting else is NULL
			default:
				$default=NULL;
			break;
		}
		
		
		$this->data[$column_name] = $default;
	}
	
	/**
	* converts the value in a field to something acceptable for
	* being saved into a database
	*/
	private function dbify($column_name)
	{
		$column=$this->columns[$column_name];
		$value=$this->data[$column_name];
		
		
		$db = database();
		
		//if the value is null, then it should be the string 'NULL'
		if ($value === NULL) return 'NULL';
		$type_ar=explode('(', $column['Type']);
		$type=strtolower($type_ar[0]);
		switch($type)
		{
			// numeric types don't have quotes around them
			case 'bit':
			case 'int':
			case 'smallint':
			case 'tinyint':
			case 'mediumint':
			case 'bigint':
			case 'real':
			case 'float':
			case 'double':
			case 'decimal':
			case 'numeric':
				return $db->sanitize("$value");
			break;
			
			// string and date types do have quotes
			case 'char':
			case 'varchar':
			case 'text':
				return "'" . $db->sanitize($value) . "'";
			break;
			
			// date values need to be converted to strings
			case 'datetime':

			break;
			case 'date':
				return "'" . $db->sanitize($value->format('Y-m-d')) . "'";
			break;
		}
	}
	/**
	*	converts values from database return strings into php values
	*/
	private function undbify($column_name, $value)
	{
		$column=$this->columns[$column_name];
		
		if ($value === 'NULL') return NULL;
		
		$type_ar=explode('(', $column['Type']);
		$type=strtolower($type_ar[0]);
		
		switch($type)
		{
			//force these to be cast to numeric values
			case 'bit':
			case 'int':
			case 'smallint':
			case 'tinyint':
			case 'mediumint':
			case 'bigint':
			case 'real':
			case 'float':
			case 'double':
			case 'decimal':
			case 'numeric':
				return  (0 + $value);
			break;
			
			case 'datetime':
			case 'timestamp':
			case 'date':
				return new DateTime($value);
			break;
			
			default:
				return $value;
			break;
		}
	}
	
}

?>